home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Power 1997 December
/
MACPOWER-1997-12.ISO.7z
/
MACPOWER-1997-12.ISO
/
AMUG
/
PROGRAMMING
/
Raven 1.2.sit
/
Raven 1.2
/
Source
/
Foundation
/
OS
/
ZCursor.cpp
< prev
next >
Wrap
Text File
|
1997-06-18
|
20KB
|
812 lines
/*
* File: ZCursor.cpp
* Summary: Mouse cursor classes.
* Written by: Jesse Jones
*
* Copyright ゥ 1996-1997 Jesse Jones.
* For conditions of distribution and use, see copyright notice in ZTypes.h
*
* Change History (most recent first):
*
* <2> 3/30/97 JDJ StillBusy only tickles the cursor if more than
* a tick has elapsed since the last tickle.
* <1> 2/24/96 JDJ Created.
*/
#include <ZCursor.h>
#include <CursorDevices.h>
#include <LowMem.h>
#include <ToolUtils.h>
#include <Traps.h>
#include <SLFunctions.h>
#include <ZDebug.h>
#include <ZExceptions.h>
#include <ZGestalt.h>
#include <ZInterruptTimer.h>
//-----------------------------------
// Constants
//
const ResID kArrowID = 0;
const ResID kIBeamID = 1;
const ResID kCrossHairID = 2;
const ResID kPlusID = 3;
const ResID kWatchID = 4;
const ResID kOpenHandID = 128;
const ResID kClosedHandID = 134;
const ResID kEyeDropperID = 129;
const ResID kBrushID = 130;
const ResID kPencilID = 131;
const ResID kSpinningWatchID = 210;
const ResID kBeachBallID = 200;
const ResID kEmptyArrowID = 220;
// ・・・ The glue code in InterfaceLib for the Cursor Devices manager is
// ・・・ハincorrect for PPC builds. Until this is fixed we'll use our
// ・・・ own glue code.
#ifndef HAS_WORKING_CURSOR_DEVICES
#define HAS_WORKING_CURSOR_DEVICES (!powerc)
#endif
#if !HAS_WORKING_CURSOR_DEVICES
enum {
uppCursorDeviceNextDeviceProcInfo =
kD0DispatchedPascalStackBased |
RESULT_SIZE(kTwoByteCode) |
STACK_ROUTINE_PARAMETER(1, kTwoByteCode) |
STACK_ROUTINE_PARAMETER(2, kFourByteCode),
uppCursorDeviceMoveProcInfo =
kD0DispatchedPascalStackBased |
RESULT_SIZE(kTwoByteCode) |
STACK_ROUTINE_PARAMETER(1, kTwoByteCode) |
STACK_ROUTINE_PARAMETER(2, kFourByteCode) |
STACK_ROUTINE_PARAMETER(3, kFourByteCode) |
STACK_ROUTINE_PARAMETER(4, kFourByteCode),
uppCursorDeviceMoveToProcInfo =
kD0DispatchedPascalStackBased |
RESULT_SIZE(kTwoByteCode) |
STACK_ROUTINE_PARAMETER(1, kTwoByteCode) |
STACK_ROUTINE_PARAMETER(2, kFourByteCode) |
STACK_ROUTINE_PARAMETER(3, kFourByteCode) |
STACK_ROUTINE_PARAMETER(4, kFourByteCode)
};
#endif
// ===================================================================================
// Helper Functions
// ===================================================================================
//---------------------------------------------------------------
//
// MyCursorDeviceNextDevice
//
//---------------------------------------------------------------
static OSErr MyCursorDeviceNextDevice(CursorDevicePtr *ourDevice)
{
#if HAS_WORKING_CURSOR_DEVICES
return CursorDeviceNextDevice(ourDevice);
#else
UniversalProcPtr trap = NGetTrapAddress(_CursorDeviceDispatch, ToolTrap);
return (OSErr) CallUniversalProc(trap, uppCursorDeviceNextDeviceProcInfo, 0x0B, ourDevice);
#endif
}
//---------------------------------------------------------------
//
// MyCursorDeviceMove
//
//---------------------------------------------------------------
static OSErr MyCursorDeviceMove(CursorDevicePtr ourDevice, long deltaX, long deltaY)
{
#if HAS_WORKING_CURSOR_DEVICES
return CursorDeviceMove(ourDevice, deltaX, deltaY);
#else
UniversalProcPtr trap = NGetTrapAddress(_CursorDeviceDispatch, ToolTrap);
return (OSErr) CallUniversalProc(trap, uppCursorDeviceMoveProcInfo, 0x00, ourDevice, deltaX, deltaY);
#endif
}
//---------------------------------------------------------------
//
// MyCursorDeviceMoveTo
//
//---------------------------------------------------------------
static OSErr MyCursorDeviceMoveTo(CursorDevicePtr ourDevice, long deltaX, long deltaY)
{
#if HAS_WORKING_CURSOR_DEVICES
return CursorDeviceMoveTo(ourDevice, deltaX, deltaY);
#else
UniversalProcPtr trap = NGetTrapAddress(_CursorDeviceDispatch, ToolTrap);
return (OSErr) CallUniversalProc(trap, uppCursorDeviceMoveToProcInfo, 0x01, ourDevice, deltaX, deltaY);
#endif
}
#pragma mark -
// ===================================================================================
// class TCursor
// ===================================================================================
const TCursor kArrowCursor(kArrowID);
const TCursor kIBeamCursor(kIBeamID);
const TCursor kCrossHairCursor(kCrossHairID);
const TCursor kPlusCursor(kPlusID);
const TCursor kWatchCursor(kWatchID);
const TCursor kOpenHandCursor(kOpenHandID);
const TCursor kClosedHandCursor(kClosedHandID);
const TCursor kEyeDropperCursor(kEyeDropperID);
const TCursor kBrushCursor(kBrushID);
const TCursor kPencilCursor(kPencilID);
const TCursor kSpinningWatchCursor(kSpinningWatchID);
const TCursor kBeachBallCursor(kBeachBallID);
const TCursor kEmptyArrowCursor(kEmptyArrowID);
//---------------------------------------------------------------
//
// TCursor::~TCursor
//
//---------------------------------------------------------------
TCursor::~TCursor()
{
if (mColorCursor != nil)
DisposeCCursor(mColorCursor);
delete mBwCursor;
}
//---------------------------------------------------------------
//
// TCursor::TCursor (ResID)
//
//---------------------------------------------------------------
TCursor::TCursor(ResID id)
{
mColorCursor = nil;
mBwCursor = nil;
mID = id;
SLDisable(); // Spotlight will complain if there's no color cursor
mColorCursor = ::GetCCursor(mID);
SLEnable();
if (mColorCursor == nil) {
mBwCursor = new Cursor;
if (mID != 0) {
CursHandle cursor = GetCursor(mID);
ThrowIfResFail(cursor);
*mBwCursor = **cursor;
HPurge((Handle) cursor);
} else
*mBwCursor = qd.arrow;
}
}
//---------------------------------------------------------------
//
// TCursor::TCursor (TCursor)
//
//---------------------------------------------------------------
TCursor::TCursor(const TCursor& rhs)
{
mColorCursor = nil;
mBwCursor = nil;
mID = rhs.mID;
if (rhs.mColorCursor != nil) {
mColorCursor = ::GetCCursor(rhs.mID);
ThrowIfResFail(mColorCursor);
}
if (rhs.mBwCursor != nil)
mBwCursor = new Cursor(*rhs.mBwCursor);
}
//---------------------------------------------------------------
//
// TCursor::operator=
//
//---------------------------------------------------------------
TCursor& TCursor::operator=(const TCursor& rhs)
{
if (this != &rhs && *this != rhs) {
CCrsrHandle colorCursor = nil;
Cursor* bwCursor = nil;
try {
if (rhs.mColorCursor != nil) {
colorCursor = ::GetCCursor(rhs.mID);
ThrowIfResFail(colorCursor);
}
if (rhs.mBwCursor != nil)
bwCursor = new Cursor(*rhs.mBwCursor);
if (mColorCursor != nil)
DisposeCCursor(mColorCursor);
delete mBwCursor;
mColorCursor = colorCursor;
mBwCursor = bwCursor;
mID = rhs.mID;
} catch(...) {
if (colorCursor != nil)
DisposeCCursor(colorCursor);
delete bwCursor;
throw;
}
}
return *this;
}
#pragma mark -
// ===================================================================================
// class ZCursorSpinner
// ===================================================================================
const MilliSecond kSpinFreq = 60;
class ZCursorSpinner : public MTimeMgrTimer {
typedef MTimeMgrTimer Inherited;
public:
virtual ~ZCursorSpinner();
ZCursorSpinner(ResID id, MilliSecond timeOut);
virtual void StartTimer();
void SetCursor(ResID newID);
void SetTimeout(MilliSecond timeOut);
MilliSecond GetTimeout() const {return (long) (1000*mDuration/60);}
protected:
virtual void OnTime();
private:
ResID mID;
Cursor* mCursors;
short mIndex;
short mNoCursors;
ulong mStartTime; // times are in ticks
ulong mDuration;
};
//---------------------------------------------------------------
//
// ZCursorSpinner::~ZCursorSpinner
//
//---------------------------------------------------------------
ZCursorSpinner::~ZCursorSpinner()
{
this->StopTimer(); // Don't want VBL task running while we tear down the object!
delete [] mCursors;
}
//---------------------------------------------------------------
//
// ZCursorSpinner::ZCursorSpinner
//
//---------------------------------------------------------------
ZCursorSpinner::ZCursorSpinner(ResID id, MilliSecond timeOut) : MTimeMgrTimer(kSpinFreq, false)
{
ASSERT(id >= 128 && id < 32767);
mCursors = nil;
mIndex = 0;
mNoCursors = 0;
mID = 0;
this->SetTimeout(timeOut);
this->SetCursor(id);
}
//---------------------------------------------------------------
//
// ZCursorSpinner::StartTimer
//
//---------------------------------------------------------------
void ZCursorSpinner::StartTimer()
{
if (mCursors != nil && mNoCursors > 0) {
ASSERT(mIndex >= 0 && mIndex < mNoCursors);
::SetCursor(&mCursors[mIndex]);
}
mStartTime = (ulong) LMGetTicks();
Inherited::StartTimer();
}
//---------------------------------------------------------------
//
// 'acur' struct
//
//---------------------------------------------------------------
#if defined(powerc) || defined(__powerc)
#pragma options align=mac68k
#endif
struct SAcurEntry {
short cursorID;
short filler;
};
struct SAcurRecord {
short count;
short filler;
SAcurEntry cursors[1]; // variable length array
};
#if defined(powerc) || defined(__powerc)
#pragma options align=reset
#endif
typedef SAcurRecord* AcurRecordPtr;
typedef AcurRecordPtr* AcurRecordHandle;
//---------------------------------------------------------------
//
// ZCursorSpinner::SetCursor
//
//---------------------------------------------------------------
void ZCursorSpinner::SetCursor(ResID newID)
{
ASSERT(newID >= 128 && newID < 32767);
if (mID != newID) {
AcurRecordHandle acurRsrc = nil;
Cursor* newCursors = nil;
bool wasRunning = this->TimerIsRunning();
try {
this->StopTimer();
acurRsrc = (AcurRecordHandle) GetResource('acur', newID);
ThrowIfResFail(acurRsrc);
HNoPurge((Handle) acurRsrc);
ThrowIfMemError();
short count = (**acurRsrc).count;
newCursors = new Cursor[count];
for (short index = 0; index < count; index++) {
ResID cursorID = (**acurRsrc).cursors[index].cursorID;
CursHandle cursor = GetCursor(cursorID);
ThrowIfResFail(cursor);
LoadResource((Handle) cursor);
newCursors[index] = **cursor;
HPurge((Handle) cursor);
}
mIndex = 0;
mNoCursors = count;
delete [] mCursors;
mCursors = newCursors;
HPurge((Handle) acurRsrc);
if (wasRunning)
this->StartTimer();
} catch (...) {
DEBUGSTR("Couldn't initialize the spinning cursor!");
delete [] newCursors;
if (acurRsrc != nil)
HPurge((Handle) acurRsrc);
// Don't rethrow
}
}
}
//---------------------------------------------------------------
//
// ZCursorSpinner::SetTimeout
//
//---------------------------------------------------------------
void ZCursorSpinner::SetTimeout(MilliSecond timeOut)
{
mDuration = (ulong) (60*timeOut/1000);
}
//---------------------------------------------------------------
//
// ZCursorSpinner::OnTime
//
//---------------------------------------------------------------
#pragma profile off
void ZCursorSpinner::OnTime() // interrupt code
{
SAFE_ASSERT(mTimerFreq > 20);
if (mCursors != nil && mNoCursors > 0) {
if (LMGetTicks() <= mStartTime + mDuration) {
if (!LMGetCrsrBusy()) {
mIndex = (short) ((mIndex + 1) % mNoCursors);
SAFE_ASSERT(mIndex >= 0);
SAFE_ASSERT(mIndex < mNoCursors);
::SetCursor(&mCursors[mIndex]);
}
}
}
}
#pragma profile reset
#pragma mark -
// ===================================================================================
// class UCursorUtils
// ===================================================================================
const MilliSecond kDefaultTimeout = 10*1000;
TCursor UCursorUtils::msBusyCursor(kSpinningWatchID);
TCursor UCursorUtils::msCurrentCursor(kArrowID);
ZCursorSpinner* UCursorUtils::msCursorSpinner = nil;
CursorDevicePtr UCursorUtils::msCursorDevice = nil;
bool UCursorUtils::msCanMoveMouse = false;
//---------------------------------------------------------------
//
// UCursorUtils::Init
//
//---------------------------------------------------------------
void UCursorUtils::Init()
{
if (msCursorSpinner == nil) {
// ZCursorSpinner does a lot of stuff that we don't want to
// do at static construction time so we'll create one the
// first time Init is called.
static ZCursorSpinner spinner(kSpinningWatchID, kDefaultTimeout);
msCursorSpinner = &spinner;
// Rather than stuffing low mem globals we'll require that
// the Cursor Devices Manager is present for moving the mouse.
// (The CDM was introduced in Feb '93 and can be installed
// on any Mac so this seems reasonable to do).
if (TrapAvailable(_CursorDeviceDispatch)) {
// Get the first cursor device.
OSErr err = MyCursorDeviceNextDevice(&msCursorDevice);
if (err == noErr && msCursorDevice != nil) {
// Assume that we can move the mouse until we learn otherwise.
msCanMoveMouse = true;
// Loop through all of the devices and see if any of them are
// absolute. If one of the devices is absolute disable mouse
// moving so weird things don't happen.
CursorDevicePtr nextDevice = msCursorDevice;
do {
if (nextDevice->devClass == kDeviceClassAbsolute)
msCanMoveMouse = false;
err = MyCursorDeviceNextDevice(&nextDevice);
} while (err == noErr && nextDevice != nil && msCanMoveMouse);
}
} else
msCanMoveMouse = false;
}
// Stop spinning the busy cursor (we do this because it's legal
// to call Init at any time and we want to switch back to an arrow).
if (msCursorSpinner->TimerIsRunning())
msCursorSpinner->StopTimer();
// Switch the cursor to an arrow.
::InitCursor();
if (msCurrentCursor.mID != kArrowID)
msCurrentCursor = TCursor(kArrowCursor);
}
//---------------------------------------------------------------
//
// UCursorUtils::SetCursor
//
//---------------------------------------------------------------
void UCursorUtils::SetCursor(const TCursor& cursor)
{
if (msCursorSpinner == nil)
UCursorUtils::Init();
if (cursor == msBusyCursor)
UCursorUtils::ForceBusy();
else {
msCursorSpinner->StopTimer();
if (cursor.mColorCursor != nil)
::SetCCursor(cursor.mColorCursor);
else
::SetCursor(cursor.mBwCursor);
}
if (msCurrentCursor != cursor)
msCurrentCursor = cursor;
}
//---------------------------------------------------------------
//
// UCursorUtils::ForceBusy ()
//
//---------------------------------------------------------------
void UCursorUtils::ForceBusy()
{
if (msCursorSpinner == nil)
UCursorUtils::Init();
if (msCurrentCursor != msBusyCursor)
msCurrentCursor = msBusyCursor;
msCursorSpinner->StartTimer();
}
//---------------------------------------------------------------
//
// UCursorUtils::ForceBusy (TCursor)
//
//---------------------------------------------------------------
void UCursorUtils::ForceBusy(const TCursor& cursor)
{
if (cursor != msBusyCursor)
UCursorUtils::InstallCustomBusyCursor(cursor);
UCursorUtils::ForceBusy();
}
//---------------------------------------------------------------
//
// UCursorUtils::StillBusy
//
//---------------------------------------------------------------
void UCursorUtils::StillBusy()
{
if (msCursorSpinner == nil)
UCursorUtils::Init();
static long lastTime = 0; // this is often called in a tight loop so we'll only tickle the cursor if enough time elapses
long time = (long) TickCount();
if (msCurrentCursor == msBusyCursor && time != lastTime) {
msCursorSpinner->StartTimer();
lastTime = time;
}
}
//---------------------------------------------------------------
//
// UCursorUtils::SetTimeout
//
//---------------------------------------------------------------
void UCursorUtils::SetTimeout(MilliSecond duration)
{
if (msCursorSpinner == nil)
UCursorUtils::Init();
ASSERT(duration > 0);
msCursorSpinner->SetTimeout(duration);
}
//---------------------------------------------------------------
//
// UCursorUtils::GetTimeout
//
//---------------------------------------------------------------
MilliSecond UCursorUtils::GetTimeout()
{
if (msCursorSpinner == nil)
UCursorUtils::Init();
return msCursorSpinner->GetTimeout();
}
//---------------------------------------------------------------
//
// UCursorUtils::InstallCustomBusyCursor
//
//---------------------------------------------------------------
void UCursorUtils::InstallCustomBusyCursor(const TCursor& newCursor)
{
if (msCursorSpinner == nil)
UCursorUtils::Init();
msBusyCursor = newCursor;
msCursorSpinner->SetCursor(msBusyCursor.mID);
}
//---------------------------------------------------------------
//
// UCursorUtils::GetPosition
//
//---------------------------------------------------------------
TPoint UCursorUtils::GetPosition()
{
GrafPtr oldPort;
GetPort(&oldPort);
GrafPtr windPort;
GetWMgrPort(&windPort);
SetPort(windPort);
Point mouse;
GetMouse(&mouse);
SetPort(oldPort);
return mouse;
}
//---------------------------------------------------------------
//
// UCursorUtils::CanMove
//
//---------------------------------------------------------------
bool UCursorUtils::CanMove()
{
if (msCursorSpinner == nil)
UCursorUtils::Init();
return msCanMoveMouse;
}
//---------------------------------------------------------------
//
// UCursorUtils::MoveTo
//
//---------------------------------------------------------------
void UCursorUtils::MoveTo(short x, short y)
{
VERIFY(UCursorUtils::CanMove());
if (msCursorDevice != nil && msCanMoveMouse) {
OSErr err = MyCursorDeviceMoveTo(msCursorDevice, x, y);
ASSERT(err == noErr);
long finalTicks; // Wait until the VBL task catches up with the new mouse position.
Delay(1, &finalTicks);
}
}
//---------------------------------------------------------------
//
// UCursorUtils::MoveBy
//
//---------------------------------------------------------------
void UCursorUtils::MoveBy(short dx, short dy)
{
VERIFY(UCursorUtils::CanMove());
if (msCursorDevice != nil && msCanMoveMouse) {
OSErr err = MyCursorDeviceMove(msCursorDevice, dx, dy);
ASSERT(err == noErr);
long finalTicks; // Wait until the VBL task catches up with the new mouse position.
Delay(1, &finalTicks);
}
}
#pragma mark -
// ===================================================================================
// class TForceBusyCursor
// ===================================================================================
//---------------------------------------------------------------
//
// TForceBusyCursor::~TForceBusyCursor
//
//---------------------------------------------------------------
TForceBusyCursor::~TForceBusyCursor()
{
if (mWasBusy)
if (mOldCursor == UCursorUtils::msBusyCursor)
UCursorUtils::ForceBusy();
else
UCursorUtils::ForceBusy(mOldCursor);
else
UCursorUtils::SetCursor(mOldCursor);
}
//---------------------------------------------------------------
//
// TForceBusyCursor::TForceBusyCursor ()
//
//---------------------------------------------------------------
TForceBusyCursor::TForceBusyCursor() : mOldCursor(UCursorUtils::msCurrentCursor)
{
if (UCursorUtils::msCursorSpinner == nil)
UCursorUtils::Init();
mWasBusy = UCursorUtils::msCursorSpinner->TimerIsRunning();
UCursorUtils::ForceBusy();
}
//---------------------------------------------------------------
//
// TForceBusyCursor::TForceBusyCursor (TCursor)
//
//---------------------------------------------------------------
TForceBusyCursor::TForceBusyCursor(const TCursor& cursor) : mOldCursor(UCursorUtils::msCurrentCursor)
{
if (UCursorUtils::msCursorSpinner == nil)
UCursorUtils::Init();
mWasBusy = UCursorUtils::msCursorSpinner->TimerIsRunning();
UCursorUtils::ForceBusy(cursor);
}